package com.coocaa.ops.admin.async.log.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.coocaa.ops.admin.async.log.model.LogInfoModel;
import com.coocaa.ops.admin.base.annotation.Auth;
import com.coocaa.ops.admin.base.annotation.Config;
import com.coocaa.ops.admin.base.language.CommonLanguageProperties;
import com.coocaa.ops.admin.v1.audit.model.ExecutionTaskModel;
import com.coocaa.ops.admin.v1.auth.sys.biz.impl.SysLogLoginBizImpl;
import com.coocaa.ops.admin.v1.auth.sys.biz.impl.SysLogOpBizImpl;
import com.coocaa.ops.admin.v1.auth.sys.biz.impl.SysMenuBizImpl;
import com.coocaa.ops.admin.v1.auth.sys.biz.impl.SysUserBizImpl;
import com.coocaa.ops.admin.v1.auth.sys.constant.MenuTokenConstants;
import com.coocaa.ops.common.core.base.enums.AuthEnum;
import com.coocaa.ops.common.core.base.model.SimpleSysUserInfo;
import com.coocaa.ops.common.core.biz.audit.entity.AuditTaskInfoEntity;
import com.coocaa.ops.common.core.biz.auth.entity.SysLogLoginEntity;
import com.coocaa.ops.common.core.biz.auth.entity.SysLogOpEntity;
import com.coocaa.ops.common.core.biz.auth.entity.SysUserEntity;
import com.coocaa.ops.common.tool.aspect.AspectUtil;
import com.coocaa.ops.common.tool.date.DateUtil;
import com.coocaa.ops.common.tool.json.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;

/**
 * @author bijiahao
 * @date : 2019/7/18.
 * @description 操作日志服务实现
 */
@Slf4j
@Service
public class LogRecordServiceImpl {
    private static final int LIMIT_FILE_SIZE = 64000;
    private static SerializeConfig mapping = new SerializeConfig();
    @Autowired
    private SysLogOpBizImpl sysLogOpBiz;
    @Autowired
    private SysMenuBizImpl sysMenuBiz;
    @Autowired
    private SysLogLoginBizImpl sysLogLoginBiz;
    @Autowired
    private SysUserBizImpl sysUserBiz;
    private static CommonLanguageProperties commonLanguageProperties;

    /**
     * 异步执行日志记录
     */
    @Async("commonExecutor")
    public void logUserLoginRecord(SysUserEntity user, String loginIp, String userAgent) {
        this.handleLogUserLogin(user, loginIp, userAgent);
    }


    @Async("commonExecutor")
    public void logOpRecord(LogInfoModel logInfoModel) {
        ProceedingJoinPoint joinPoint = logInfoModel.getJoinPoint();
        if (this.isNeedRecord(joinPoint)) {
            SysLogOpEntity sysLogOpEntity = getSysLogOpEntity(logInfoModel);
            if (isSizeTooLarge(sysLogOpEntity)) {
                log.warn(String.format("操作日志：%s", JSON.toJSONString(sysLogOpEntity)));
            } else {
                sysLogOpBiz.add(sysLogOpEntity);
            }
        }
    }

    /**
     * 日志记录-打开任务
     *
     * @param taskInfo
     * @param costTime
     */
    @Async("commonExecutor")
    public void logOpRecord(AuditTaskInfoEntity taskInfo, Long costTime, SysUserEntity user) {
        try {
            SysLogOpEntity sysLogOpEntity = getSysLogOpEntityForOpenTask(taskInfo, costTime, user);
            sysLogOpBiz.add(sysLogOpEntity);
        } catch (Exception e) {
            log.error("openTask 操作日志保存异常: ", e);
        }
    }

    @Async("commonExecutor")
    public void logOpRecord(ExecutionTaskModel executionTaskModel, SimpleSysUserInfo userInfo, Long costTime) {
        SysLogOpEntity sysLogOpEntity = this.getSysLogOpEntityForAuditTask(executionTaskModel, userInfo, costTime);
        sysLogOpBiz.add(sysLogOpEntity);
    }


    /*************** ===私有方法=== ********************* ===start=== ***************************************/

    private SysLogOpEntity getSysLogOpEntity(LogInfoModel logInfoModel) {
        final SysUserEntity user = logInfoModel.getUserEntity();
        //创建 系统日志操作实体
        SysLogOpEntity sysLogOpEntity = new SysLogOpEntity();
        sysLogOpEntity.setUserId(user.getId());
        sysLogOpEntity.setUserName(user.getUserName());
        sysLogOpEntity.setElapsedTime(logInfoModel.getCostTime().floatValue() / 1000);
        //获取用户操作的菜单
        ProceedingJoinPoint joinPoint = logInfoModel.getJoinPoint();
        Config config = joinPoint.getTarget().getClass().getAnnotation(Config.class);
        String[] menuElIds = config.menuToken();
        Assert.notEmpty(menuElIds, "=== Contoller class->Config->ELID" + commonLanguageProperties.getNotNull() + "===");


        String menuElId = menuElIds[0];
        //设置 菜单元素id
        sysLogOpEntity.setMenuToken(menuElId);
        //根据菜单 元素id 获取菜单名称
        //设置 菜单名称
        sysLogOpEntity.setMenuName(this.getMenuName(menuElIds));
        //设置 执行的方法
        sysLogOpEntity.setExecMethod(AspectUtil.getMethod(joinPoint).getName());
        //设置操作数据
        sysLogOpEntity.setOpData(this.getOpData(joinPoint.getArgs(), logInfoModel.getReturnValue()));
        return sysLogOpEntity;
    }

    @Autowired
    public void setCommonLanguageProperties(CommonLanguageProperties commonLanguageProperties) {
        LogRecordServiceImpl.commonLanguageProperties = commonLanguageProperties;
    }

    private String getOpData(Object[] args, Object returnValue) {
        Map<String, Object> map = this.returnValue2Map(returnValue);
        //如果【success】返回值为false
        String opDataStr = "";
        boolean success = this.isReturnSuccess(map);
        if (!success) {
            opDataStr = (String) map.get("detailMsg");
            if (null == opDataStr) {
                opDataStr = (String) map.get("msg");
            }
            return opDataStr;
        }
        StringBuffer sb = new StringBuffer();
        for (Object arg : args) {
            //如果执行方法 参数的实例不为HttpServletRequest
            if (arg instanceof HttpServletRequest) {
                continue;
            } else {
                sb.append(JSON.toJSONString(arg, mapping, SerializerFeature.DisableCircularReferenceDetect));
                sb.append(",");
            }
        }
        opDataStr = sb.toString();
        //去掉 多余的“，”
        if (opDataStr.endsWith(",")) {
            opDataStr = opDataStr.substring(0, opDataStr.length() - 1);
        }
        return opDataStr;
    }

    /**
     * 返回结果转化Map
     *
     * @param returnValue
     * @return
     */
    private Map<String, Object> returnValue2Map(Object returnValue) {
        //将方法返回值转化为map
        if (returnValue instanceof ResponseEntity) {
            returnValue = ((ResponseEntity) returnValue).getBody();
            if (returnValue instanceof Map) {
                return (Map<String, Object>) returnValue;
            } else {
                return JsonUtil.json2MapObj(returnValue.toString());
            }
        }
        if (returnValue instanceof Map) {
            return (Map<String, Object>) returnValue;
        }
        return JsonUtil.json2MapObj(returnValue.toString());
    }

    private boolean isReturnSuccess(Map<String, Object> map) {
        return "0".equals(map.get("code"))
                || (null != map.get("success") && (Boolean) (map.get("success")));
    }


    private SysLogOpEntity getSysLogOpEntityForOpenTask(AuditTaskInfoEntity taskInfo, Long costTime, SysUserEntity user) {
        //创建 系统日志操作实体
        SysLogOpEntity sysLogOpEntity = new SysLogOpEntity();
        sysLogOpEntity.setMenuToken("openTask");
        sysLogOpEntity.setMenuName("OpenTask");
        sysLogOpEntity.setExecMethod(taskInfo.getResourceType() + "-" + "openTask");
        sysLogOpEntity.setUserId(user.getId());
        sysLogOpEntity.setUserName(user.getUserName());
        sysLogOpEntity.setElapsedTime(costTime.floatValue() / 1000);
        //只需要关键操作数据
        AuditTaskInfoEntity opData = new AuditTaskInfoEntity();
        opData.setResourceId(taskInfo.getResourceId());
        opData.setResourceType(taskInfo.getResourceType());
        opData.setResourceVersion(taskInfo.getResourceVersion());
        opData.setCurrentOperator(taskInfo.getCurrentOperator());
        opData.setCreator(taskInfo.getCreator());
        opData.setSource(taskInfo.getSource());
        sysLogOpEntity.setOpData(JsonUtil.obj2Json(opData));
        return sysLogOpEntity;
    }

    private boolean isNeedRecord(ProceedingJoinPoint joinPoint) {
        //获取菜单配置 注解
        Config config = joinPoint.getTarget().getClass().getAnnotation(Config.class);
        if (null == config) {
            return false;
        }
        Method method = AspectUtil.getMethod(joinPoint);
        //获取方法的权限 注解
        Auth auth = method.getAnnotation(Auth.class);
        if (null == auth) {
            return false;
        }
        return isNeedRecord(auth);

    }

    private boolean isNeedRecord(Auth auth) {
        AuthEnum authType = auth.authorityType();
        return AuthEnum.SAVE == authType
                || AuthEnum.DELETE == authType
                || AuthEnum.AUDIT == authType
                || AuthEnum.SHELVES == authType
                || AuthEnum.BATCHAUDIT == authType
                || AuthEnum.COLLECT == authType
                ;
    }

    /**
     * 处理用户登录操作记录
     *
     * @param user
     * @param loginIp
     * @param userAgent
     */
    private void handleLogUserLogin(SysUserEntity user, String loginIp, String userAgent) {
        Date loginTime = DateUtil.getCurDate();
        SysUserEntity upUserEntity = new SysUserEntity();
        upUserEntity.setId(user.getId());
        upUserEntity.setLoginTime(loginTime);
        upUserEntity.setLoginIp(loginIp);
        upUserEntity.setLoginCount(user.getLoginCount() + 1);
        try {
            sysUserBiz.updateById(upUserEntity);
            SysLogLoginEntity login = new SysLogLoginEntity();
            login.setUserName(user.getLoginName());
            login.setUserId(user.getId());
            login.setLoginTime(loginTime);
            login.setLoginIp(loginIp);
            login.setLoginType(0);
            login.setUserAgent(userAgent);
            login.setRemark("log in");
            sysLogLoginBiz.add(login);
        } catch (Exception e) {
            log.error("用户登录时写入数据失败！", e);
        }
    }

    /**
     * 审核任务-记录
     *
     * @param executionTaskModel
     * @param user
     * @param costTime
     * @return
     */
    private SysLogOpEntity getSysLogOpEntityForAuditTask(ExecutionTaskModel executionTaskModel, SimpleSysUserInfo user, Long costTime) {
        SysLogOpEntity sysLogOpEntity = new SysLogOpEntity();
        sysLogOpEntity.setMenuToken("auditTaskInfo");
        sysLogOpEntity.setMenuName("审核任务");
        sysLogOpEntity.setExecMethod(executionTaskModel.getType() + "-auditTask");
        sysLogOpEntity.setUserId(user.getUserId());
        sysLogOpEntity.setUserName(user.getUserName());
        sysLogOpEntity.setElapsedTime(costTime.floatValue() / 1000);
        sysLogOpEntity.setCreatedDate(new Date());
        sysLogOpEntity.setOpData(JSON.toJSONString(executionTaskModel));
        return sysLogOpEntity;
    }


    /**
     * 操作数据太大 <code> >= 64kb </code>
     *
     * @param sysLogOpEntity
     * @return
     */
    private boolean isSizeTooLarge(SysLogOpEntity sysLogOpEntity) {
        int size = sysLogOpEntity.getOpData() == null ? 0 : sysLogOpEntity.getOpData().length();
        return size >= LIMIT_FILE_SIZE;
    }

    /**
     * 获取菜单名称
     *
     * @param menuElIds
     * @return
     */
    private String getMenuName(String[] menuElIds) {
        if (MenuTokenConstants.BLOCK_INFO.equals(menuElIds[0])) {
            return "推荐位";
        }
        if (MenuTokenConstants.AUDIT_TASK_INFO.equals(menuElIds[0])) {
            return "审核任务";
        }
        return sysMenuBiz.getMenuNameByByCategory(menuElIds);
    }

}
